﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Syspetro.Core;
using Syspetro.Core.CommandBus;
using Syspetro.Core.Encrypt;
using Syspetro.Core.Extensions;
using Syspetro.Core.HttpService;
using Syspetro.Core.SysInterface;
using System.Linq;
using System.Reflection;

namespace Syspetro.ServiceCore
{
    public static class Deploy
    {
        public static IHostBuilder AddSyspetro(this IHostBuilder host, string AssemblyMatch = null)
        {
            host.ConfigureServices((hostBuilderContext, serviceCollection) => {
                serviceCollection.InitApp(hostBuilderContext.Configuration);
            });
            host.UseSerilogDefault();
            InternalApp.ServiceCollection.AddCommandMap(AssemblyMatch);
            InternalApp.ServiceCollection.AddDataService();
            return host;
        }
        /// <summary>
        /// 自动注入IOC
        /// </summary>
        /// <param name="services"></param>
        /// <param name="AssemblyMatch"></param>
        /// <returns></returns>
        public static IServiceCollection AddDataService(this IServiceCollection services, string AssemblyMatch = null)
        {
            var referencedAssemblies = AssemblyHelper.LoadFroms(AssemblyMatch);
            var baseType = typeof(IService);
            var ss = referencedAssemblies.SelectMany(o => o.GetTypes());

            var types = referencedAssemblies
                .SelectMany(a => a.DefinedTypes)
                .Select(type => type.AsType())
                .Where(x => x != baseType && baseType.IsAssignableFrom(x)).ToList();
            var implementTypes = types.Where(x => x.IsClass).ToList();
            var interfaceTypes = types.Where(x => x.IsInterface).ToList();
            foreach (var implementType in implementTypes)
            {
                if (typeof(IScopeService).IsAssignableFrom(implementType))
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddScoped(interfaceType, implementType);
                }
                else if (typeof(ISingletonService).IsAssignableFrom(implementType))
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddSingleton(interfaceType, implementType);
                }
                else if (typeof(ITransientService).IsAssignableFrom(implementType))
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddTransient(interfaceType, implementType);
                }
            }
            return services;
        }
        /// <summary>
        /// 自动注入CommandMap
        /// </summary>
        /// <param name="services"></param>
        /// <param name="AssemblyMatch"></param>
        /// <returns></returns>
        public static IServiceCollection AddCommandMap(this IServiceCollection services, string AssemblyMatch = null)
        {
            var referencedAssemblies = AssemblyHelper.LoadFroms(AssemblyMatch);
            var TypeICommand = typeof(ICommand);
            var TypeICommandHandler = typeof(ICommandHandler<>);
            var ss = referencedAssemblies.SelectMany(o => o.GetTypes());
            var Commands = referencedAssemblies
                .SelectMany(a => a.DefinedTypes)
                .Select(type => type.AsType())
                .Where(x => x != TypeICommand && TypeICommand.IsAssignableFrom(x)).ToList();

            var CommandHandlers = referencedAssemblies
                .SelectMany(a => a.DefinedTypes)
                .Select(type => type.AsType())
                .Where(t => t.GetInterfaces().Any(a => a.GetTypeInfo().IsGenericType && a.GetGenericTypeDefinition() == TypeICommandHandler))
                .ToList();

            var ICommands = Commands.Where(t => t.IsInterface).ToList();
            var interfaceTypes = CommandHandlers.Where(t => t.IsInterface).ToList();

            var _Commands = Commands.Where(t => t.IsClass).ToList();
            var _CommandHandlers = CommandHandlers.Where(t => t.IsClass).ToList();

            foreach (var implementType in _CommandHandlers)
            {
                if (typeof(IScopeService).IsAssignableFrom(implementType))
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddScoped(interfaceType, implementType);
                }
                else if (typeof(ISingletonService).IsAssignableFrom(implementType))
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddSingleton(interfaceType, implementType);
                }
                else if (typeof(ITransientService).IsAssignableFrom(implementType))
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddTransient(interfaceType, implementType);
                }
            }

            foreach (var comd in _Commands)
            {
                string key = comd.FullName;
                var ch = interfaceTypes.Where(t => t.GetInterfaces().Any(a => a.GetGenericArguments().Any(aa => aa == comd))).ToList();
                if (ch != null)
                    CommandMap.Add(key, ch);
            }
            services.AddTransient<ICommandBus, CommandBus>();

            services.AddHttpClient();
            services.AddTransient<HttpHelper>();
            services.AddTransient<IHttpCommandHandler, HttpCommandHandler>();
            services.AddTransient<IPswdCheckCommandHandler, PswdCheckCommandHandler>();
            services.AddTransient<IPswdMd5CommandHandler, PswdMd5CommandHandler>();
            CommandMap.Add(typeof(HttpCommand).FullName, typeof(IHttpCommandHandler));
            CommandMap.Add(typeof(PswdCheckCommand).FullName, typeof(IPswdCheckCommandHandler));
            CommandMap.Add(typeof(PswdMd5Command).FullName, typeof(IPswdMd5CommandHandler));

            return services;
        }
    }
}
